package com.lvyp.server.config.security;

import com.lvyp.server.config.filter.CustomFilter;
import com.lvyp.server.config.filter.CustomUrlDecisionManager;
import com.lvyp.server.pojo.Admin;
import com.lvyp.server.pojo.RespBean;
import com.lvyp.server.service.IAdminService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.annotation.Resource;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Resource
    private IAdminService adminService;
    @Resource
    private RestAuthorizationEntryPoint restAuthorizationEntryPoint;
    @Resource
    private RestfulAccessDeniedHeader restfulAccessDeniedHeader;
    @Resource
    private CustomFilter customFilter;
    @Resource
    private CustomUrlDecisionManager customUrlDecisionManager;



    @Override
    public void configure(WebSecurity web) throws Exception {
        // 放行路径
        web.ignoring().antMatchers(
                "/login",
                "/logout",
                "/css/**",
                "/js/**",
                "/index.html",
                "favicon.ico",
                "/doc.html",
                "/webjars/**",
                "/swagger-resources/**",
                "/v2/api-docs/**",
                "/captcha",
                "ws/**"
        );
    }

    /**
     *
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 验证请求
        http.authorizeRequests()
                .anyRequest().authenticated()    // 除了放行的路径，其他的路径都要验证token
                // 动态权限配置
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O o) {
                        o.setAccessDecisionManager(customUrlDecisionManager);
                        o.setSecurityMetadataSource(customFilter);
                        return o;
                    }
                });

        // 添加jwt的登录授权拦截器
        http.addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);

        // 添加自定义未授权和未登录结果返回
        http.exceptionHandling()
                .accessDeniedHandler(restfulAccessDeniedHeader)  // 权限不足结果返回
                .authenticationEntryPoint(restAuthorizationEntryPoint);  // 未登录结果返回

        // 禁用缓存
        http.headers().cacheControl();
        // 使用jwt,不需要csrf
        http.csrf().disable();
        // 基于token，不需要session
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

    }

    /**
     * 设置执行自定义登录认证
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 配置使用自己实现的userDetailsService
        auth.userDetailsService(userDetailsService())
            .passwordEncoder(passwordEncoder());

    }

    /**
     * 重写UserDetailsService
     * @return
     *
     */
    @Override  // 重写了该方法的loadUserByUsername，自己实现根据用户名获取用户信息
    @Bean
    public UserDetailsService userDetailsService() {

        return (username) -> {  // lambda 重写了该方法的loadUserByUsername：lambda表达式，参数传入username
            Admin admin = adminService.getAdminByUserName(username);  // 查询数据库根据用户名获取用户信息，返回用户信息
            if (admin != null) {
                admin.setRoles(adminService.getRoles(admin.getId()));
                return admin;  // admin类实现了userDetails，返回用户信息:  address=香港特别行政区强县长寿柳州路p座123, enabled=true, username=admin, password=$2a$10$ogvUqZZAxrBwrmVI/e7.SuFYyx8my8d.9zJ6bs9lPKWvbD9eefyCe, userFace=http://192.168.10.100:8888/group1/M00/00/00/wKgKZF6oHzuAXnw9AABaLsrkrQQ148.jpg, remark=null)
            }
            throw new UsernameNotFoundException("用户名或密码不正确");
        };
    }

    @Bean
    public PasswordEncoder passwordEncoder (){
        return new BCryptPasswordEncoder();
    }

    @Bean
    public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter (){
        return new JwtAuthenticationTokenFilter();
    }
}
